package com.edouardbataille.open
{
	import __AS3__.vec.Vector;
	
	import com.edouardbataille.open.bytes.ByteReader;
	import com.edouardbataille.open.handler.IHandler;
	import com.edouardbataille.open.handler.InitHandler;
	import com.edouardbataille.open.requester.PreparedStatement;
	import com.edouardbataille.open.requester.Query;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;

	[Event (name="connect", type="flash.events.Event")]
	[Event (name="close", type="flash.events.Event")]
	[Event (name="ioError", type="flash.events.IOERrorEvent")]
	public class MySqlSocket extends EventDispatcher
	{
		
		private var scrumble : String = "";

		private var server_language : uint;

		private var wasInit : Boolean = false;
		
		private var isConnected : Boolean = false;

		private var handler : IHandler;
		
		private var socket : Socket;
		
		private var _working : Boolean;
		
		private var currentBa : ByteReader;
		
		private var sendBuffer : Array = new Array ();
		
		private var packets : Vector.<ByteReader>;

		public function MySqlSocket (host : String = null, port : int = 0, login : String = null, pass : String = null, catalog : String=null)
		{
			socket = new Socket ();
			socket.addEventListener(Event.CONNECT, connectHandler);
			socket.addEventListener(Event.CLOSE, closeHandler);
			socket.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, receiveDataHandler);
			handler = new InitHandler (this, login, pass, catalog);
			socket.connect(host, port);
		}
		
		private function set working (value : Boolean) : void
		{
			_working = value;
			if (value == false && sendBuffer.length > 0)
			{
				var obj : Object = sendBuffer.shift();
				send(obj.response as ByteArray, obj.handler as IHandler, obj.numPacket as uint);
			}
			
		} 

		private function connectHandler (event : Event) : void
		{
		}
		
		private function closeHandler (event : Event) : void
		{
			dispatchEvent(event);
		}

		private function errorHandler (event : IOErrorEvent) : void
		{
			if (hasEventListener(event.type))
				dispatchEvent(event);
			else
				throw new Error (event.text);
		}
		
		private function receiveDataHandler (event : ProgressEvent) : void
		{
			working = true;
			/* if (!readBuffer)
				readBuffer = new ByteReader (); */
			if (!packets)
				packets = new Vector.<ByteReader> ();
			if (!currentBa)
				currentBa = new ByteReader ();
			var isOk : Boolean = true;
			socket.readBytes(currentBa, currentBa.length, socket.bytesAvailable);
			while (isOk && currentBa.bytesAvailable >= 4)
			{
				var rowLength : uint = currentBa.readPacketLength();
				var rowPacket : uint = currentBa.readPacketNum();
				currentBa.position -= 4;
				if (currentBa.bytesAvailable - 4 < rowLength)
				{
					isOk = false;
					break;
				}
				var tmp : ByteReader = new ByteReader ();
				currentBa.readBytes(tmp, 0, rowLength + 4);
				packets.push (tmp);
			}
			if (isOk)
			{
				if (currentBa.bytesAvailable == 0)
					currentBa = null;
				handler.handleData(packets, this);
				packets = null;
				working = false;
			}
		}
		
		private function checkBa () : Boolean
		{
			while (currentBa.bytesAvailable >= 4)
			{
				var rowLength : uint = currentBa.readPacketLength();
				var rowPacket : uint = currentBa.readPacketNum();
				if (currentBa.bytesAvailable < rowLength)
				{
					currentBa.position -= 4;
					break;
				}
				currentBa.position += rowLength;
			}
			var res : Boolean = currentBa.bytesAvailable == 0;
			return res;
		}

		public function send (response : ByteArray, handler : IHandler, numPacket : int=0) : void
		{
			if (_working)
			{
				if (!handler)
					handler = this.handler;
				sendBuffer.push({response:response, handler:handler, numPacket:numPacket});
				return;
			}
			working = true;
			this.handler = handler;
			response.position = 0;
			var baToSend : ByteArray = new ByteArray();
			var available : uint = response.length;
			var length : uint = available;
			baToSend.writeByte(length & 0xFF);
			length = available >>> 8;
			baToSend.writeByte(length & 0xFF);
			length = available >>> 16;
			baToSend.writeByte(length & 0xFF);
			baToSend.writeByte(numPacket);
			response.readBytes(baToSend, 4, response.bytesAvailable);
			baToSend.position = 0;
			socket.writeBytes(baToSend, 0, baToSend.bytesAvailable);
			socket.flush();
			return;
		}
		
		public function isConnect (value : Boolean) : void
		{
			handler = null;
			isConnected = value;
			if (isConnected)
				dispatchEvent(new Event (Event.CONNECT));
		}
		
		public function getPreparedStatement () : PreparedStatement
		{
			var stmt : PreparedStatement = new PreparedStatement (this);
			return stmt;
		}
		
		public function getQuery () : Query
		{
			var res : Query = new Query (this);
			return res;
		}
	}
}